package net.sf.rose.jdbc;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.commons.lang.StringUtils;

import net.sf.rose.util.BeanWrapper;
import net.sf.rose.util.DateFormat;

/**
 * 类说明：主键生成器
 * @author fengjian
 * @since 2014-6-17
 */
public class KeyGenerator {

	/** 日期格式化类 */
	private static final SimpleDateFormat _dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");

	/** 记数器值缓存 */
	private static int _count = 1000;

	public static String GENERATOR_UUID_TYPE = "UUID";

	/**
	 * 生成主键
	 * 主键长度为18位
	 * @return - 主键
	 */
	public static String getSimpleUUID() {
		// 从缓存中取出此前缀相关的记数，如果没有或大于9999，则置1000
		_count =  _count > 9999 ? 1000 : ++_count;
		return _dateFormat.format(new Date()) + _count;
	}

	/**
	 * 生成主键
	 * 主键长度为32位
	 * @return - 主键
	 */
	public static String getSystemUUID() {
		String str = UUID.randomUUID().toString();
		return str.substring(0, 8) + str.substring(9, 13) + str.substring(14, 18) + str.substring(19, 23) + str.substring(24);
	}

	/** 记数器值缓存 */
	private static final Map<String, Integer> countMap = new HashMap<String, Integer>();

	/**
	 * 生成主键
	 * @param beanName - 主键首位标识:取类名的前四位作为前缀，如果类名称不满四位长则取全部
	 * @return - 主键
	 */
	public static String uuid(String beanName) {
		// 取类名的前四位作为前缀，如果类名称不满四位长则取全部
		String pre = beanName.length() > 4 ? beanName.substring(0, 4) : beanName;
		pre = pre.toUpperCase();
		// 从缓存中取出此前缀相关的记数，如果没有或大于9999，则置1000
		Integer count = countMap.get(pre);
		count = count == null || count > 9999 ? 1000 : ++count;
		countMap.put(pre, count);
		return pre + _dateFormat.format(new Date()) + count;
	}

	/** 日期格式化类 */
	private DateFormat dateFormat = null;

	/** 记数器值缓存 */
	private long count = 0;

	/** 日期值缓存 */
	private String day = null;

	/** 主键格式类型 */
	private char[] type = null;

	/** 主键格式字符串 */
	private String[] pattern = null;

	/** 变量名数组 */
	private String[] paramemters = null;

	/** 记数器值缓存 */
	private boolean needInitialCount = false;

	/** 是否使用UUID主键生成策略 */
	private boolean uuidUsed = false;

	/** 仅仅使用有序数字作为主键生成策略 */
	private boolean intUsed = false;

	/**
	 * 是否需要初始化计数器
	 * @return 是否需要初始化计数器
	 */
	public boolean isNeedInitialCount() {
		return needInitialCount;
	}

	/**
	 * 是否需要使用变量
	 * @return 是否需要使用变量
	 */
	public boolean isNeedParamemters() {
		return paramemters != null;
	}

	/**
	 * 生成主键 {yyyyMMdd}+[0001]+(****)+EN+$username$
	 * {yyyyMMdd} - 日期必须在有序数字前,因为有序数字的计数器以日期为基准
	 * [0001] - 有序数字
	 * (****) - 无序数字
	 * EN - 固定字符
	 * $username$ - 变量名
	 * @param format  - 主键格式
	 */
	public void setkeyGenerateString(String format) {
		if (format == null) {
			return;
		}
		// 如果是使用UUID主键生成策略
		if (format.equals(GENERATOR_UUID_TYPE)) {
			uuidUsed = true;
			return;
		} else {
			uuidUsed = false;
		}
		// 如果是仅仅使用有序数字作为主键生成策略
		if (format.charAt(0) == '[' && format.endsWith("]")) {
			intUsed = true;
			needInitialCount = true;
			return;
		} else {
			intUsed = false;
		}
		String[] arr = StringUtils.split(format, '+');
		type = new char[arr.length];
		pattern = new String[arr.length];
		List<String> params = new ArrayList<>();
		for (int i = 0; i < arr.length; i++) {
			char c = arr[i].charAt(0);
			// 日期格式
			if (c == '{') {
				type[i] = 't';
				pattern[i] = arr[i].substring(1, arr[i].length() - 1);
				dateFormat = new DateFormat(pattern[i]);
			}
			// 有序数字
			else if (c == '[') {
				type[i] = 's';
				pattern[i] = arr[i].substring(1, arr[i].length() - 1);
				pattern[i] = "0000000000".substring(0, pattern[i].length());
				needInitialCount = true;
			}
			// 无序数字
			else if (c == '(') {
				type[i] = 'd';
				pattern[i] = arr[i].substring(1, arr[i].length() - 1);
				pattern[i] = "1000000000".substring(0, pattern[i].length());
			// 变量
			} else if (c == '$') {
				type[i] = 'v';
				params.add(arr[i].substring(1, arr[i].length() - 1));
			}
			// 字符
			else {
				type[i] = 'c';
				pattern[i] = arr[i];
			}
		}
		if (params.size() > 0) {
			paramemters = new String[params.size()];
			for (int i = 0; i < params.size(); i++) {
				paramemters[i] = params.get(i);
			}
		}
	}

	/**
	 * 生成主键
	 * @return - 主键
	 */
	public synchronized Object uuid() {
		// 如果是使用UUID主键生成策略
		if (uuidUsed) {
			String str = UUID.randomUUID().toString();
			return str.substring(0, 8) + str.substring(9, 13) + str.substring(14, 18) + str.substring(19, 23) + str.substring(24);
		}
		// 如果是仅仅使用有序数字作为主键生成策略
		if (intUsed) {
			this.count++;
			return this.count;
		}
		String thisDay = null;
		StringBuilder buf = new StringBuilder();
		for (int i = 0; i < type.length; i++) {
			char c = type[i];
			// 日期格式
			if (c == 't') {
				thisDay = dateFormat.format(new Date());
				buf.append(thisDay);
			}
			// 有序数字
			else if (c == 's') {
				// 如果日期存在
				if (day != null) {
					// 如果当前日期与缓存日期一致，计数器累计
					if (thisDay.equals(day)) {
						count++;
					}
					// 如果不一致，重新计算
					else {
						count = 1;
					}
				}
				// 如果日期不存在，重新计算
				else {
					count++;
				}
				String str = pattern[i] + count;
				buf.append(str.substring(str.length() - pattern[i].length(), str.length()));
			}
			// 无序数字
			else if (c == 'd') {
				buf.append(((int) ((Math.random() * 9 + 1) * Integer.parseInt(pattern[i]))));
			}
			// 字符
			else {
				buf.append(pattern[i]);
			}
		}
		// 将当前日期缓存
		this.day = thisDay;
		return buf.toString();
	}

	/**
	 * 生成主键
	 * @param bean - 参数对象
	 * @return - 主键
	 */
	public synchronized Object uuid(Object bean) {
		// 如果是使用UUID主键生成策略
		if (uuidUsed) {
			String str = UUID.randomUUID().toString();
			return str.substring(0, 8) + str.substring(9, 13) + str.substring(14, 18) + str.substring(19, 23) + str.substring(24);
		}
		// 如果是仅仅使用有序数字作为主键生成策略
		if (intUsed) {
			this.count++;
			return this.count;
		}
		int varCount = 0;
		BeanWrapper bw = new BeanWrapper(bean);
		String thisDay = null;
		StringBuilder buf = new StringBuilder();
		for (int i = 0; i < type.length; i++) {
			char c = type[i];
			// 日期格式
			if (c == 't') {
				thisDay = dateFormat.format(new Date());
				buf.append(thisDay);
			}
			// 有序数字
			else if (c == 's') {
				// 如果日期存在
				if (day != null) {
					// 如果当前日期与缓存日期一致，计数器累计
					if (day.equals(thisDay)) {
						count++;
					}
					// 如果不一致，重新计算
					else {
						count = 1;
					}
				}
				// 如果日期不存在，重新计算
				else {
					count++;
				}
				String str = pattern[i] + count;
				buf.append(str.substring(str.length() - pattern[i].length(), str.length()));
			}
			// 无序数字
			else if (c == 'd') {
				buf.append(((int) ((Math.random() * 9 + 1) * Integer.parseInt(pattern[i]))));
			}
			// 变量
			else if (c == 'v') {
				Object obj = bw.get(paramemters[varCount]);
				if (obj != null) {
					buf.append(obj.toString());
				}
				varCount++;
			}
			// 字符
			else {
				buf.append(pattern[i]);
			}
		}
		// 将当前日期缓存
		this.day = thisDay;
		return buf.toString();
	}

	/**
	 * 生成主键
	 * @param map - 参数容器
	 * @return - 主键
	 */
	public synchronized Object uuid(Map<String, Object> map) {
		// 如果是使用UUID主键生成策略
		if (uuidUsed) {
			String str = UUID.randomUUID().toString();
			return str.substring(0, 8) + str.substring(9, 13) + str.substring(14, 18) + str.substring(19, 23) + str.substring(24);
		}
		// 如果是仅仅使用有序数字作为主键生成策略
		if (intUsed) {
			this.count++;
			return this.count;
		}
		int varCount = 0;
		String thisDay = null;
		StringBuffer buf = new StringBuffer();
		for (int i = 0; i < type.length; i++) {
			char c = type[i];
			// 日期格式
			if (c == 't') {
				thisDay = dateFormat.format(new Date());
				buf.append(thisDay);
			}
			// 有序数字
			else if (c == 's') {
				// 如果日期存在
				if (day != null) {
					// 如果当前日期与缓存日期一致，计数器累计
					if (thisDay.equals(day)) {
						count++;
					}
					// 如果不一致，重新计算
					else {
						count = 1;
					}
				}
				// 如果日期不存在，重新计算
				else {
					count++;
				}
				String str = pattern[i] + count;
				buf.append(str.substring(str.length() - pattern[i].length(), str.length()));
			}
			// 无序数字
			else if (c == 'd') {
				buf.append(((int) ((Math.random() * 9 + 1) * Integer.parseInt(pattern[i]))));
			}
			// 变量
			else if (c == 'v') {
				Object obj = map.get(paramemters[varCount]);
				if (obj != null) {
					buf.append(obj.toString());
				}
				varCount++;
			}
			// 字符
			else {
				buf.append(pattern[i]);
			}
		}
		// 将当前日期缓存
		this.day = thisDay;
		return buf.toString();
	}

	/**
	 * 设置有序数字初始值
	 * @param format - 格式化类型
	 * @return 计数器值
	 */
	public long initialCount(String format) {
		// 如果使用UUID主键生成策略，不需要初始化
		if (uuidUsed) {
			return -1;
		}
		// 如果是仅仅使用有序数字作为主键生成策略
		if (intUsed) {
			this.count = Long.parseLong(format);
			return this.count;
		}
		int s1 = 0, e1 = 0, s2 = 0, e2 = 0;
		boolean flag = false;
		// 从格式中取日期值
		for (int i = 0; i < type.length; i++) {
			// 日期格式
			if (type[i] == 't') {
				if (flag) {
					s1 = e2;
				}
				flag = true;
				e1 = s1 + pattern[i].length();
			} else {
				// 有序数字
				if (type[i] == 's') {
					if (flag) {
						s2 = e1;
					} else {
						s2 = s1;
					}
					flag = true;
					e2 = s2 + pattern[i].length();
				} else {
					s1 = s1 + pattern[i].length();
				}
			}
		}
		if (s1 != e1) {
			String maxDay = format.substring(s1, e1);
			String thisDay = dateFormat.format(new Date());
			if (maxDay.equals(thisDay)) {
				String str = format.substring(s2, e2);
				this.count = Integer.parseInt(str);
			}
		}
		return this.count;
	}

	/**
	 * 设置有序数字减1
	 */
	public void backCount() {
		this.count = this.count > 1 ? this.count - 1 : 0;
	}

	/**
	 * 测试代码
	 * @param args - 启动参数
	 * @throws Exception 发生异常时抛出
	 */
	public static void main(String[] args) throws Exception {
		String type = "EN+{yyyyMMdd}+[0001]+(****)";
		KeyGenerator key = new KeyGenerator();
		key.setkeyGenerateString(type);
		System.out.println("规则：固定字符串+日期+顺序数字+随机数字：" + key.uuid());
		type = "EN+{yyyyMMdd}+[01]";
		key.setkeyGenerateString(type);
		System.out.println("规则：固定字符串+日期+顺序数字：" + key.uuid());
		key.initialCount("EN2015030103");
		System.out.println("初始化自增变量：" + key.uuid());
		// DBAUser user = new DBAUser();
		// user.setUserID("ABC");
		type = "EN+{yyyyMMdd}+$userID$+[01]";
		key.setkeyGenerateString(type);
		// System.out.println("规则：固定字符串+日期+对象属性+顺序数字：" + key.uuid(user));
		key.setkeyGenerateString("UUID");
		System.out.println("规则：UUID：" + key.uuid());
		key.setkeyGenerateString("[000001]");
		key.initialCount("002");
		System.out.println("规则：UUID：" + key.uuid());
		key.setkeyGenerateString("$parentID$+[001]");
		//MenuBean bean = new MenuBean();
		//bean.setParentID("ROT001");
		//System.out.println(key.uuid(bean));
	}
}
